package com.db4o.odbgen.plugins.java.database;

import java.util.ArrayList;
import java.util.List;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;


import com.db4o.internal.odbgen.TemplatesUtils;
import com.db4o.internal.odbgen.plugins.TemplateList;
import com.db4o.internal.odbgen.plugins.java.database.NotnullXmlSchemaElement;
import com.db4o.odbgen.OdbgenException;
import com.db4o.odbgen.TargetLanguage;
import com.db4o.odbgen.plugins.MainGeneratorPlugin;
import com.db4o.odbgen.plugins.Plugin;
import com.db4o.odbgen.plugins.java.maingenerator.MainGenerator;
import com.db4o.odbgen.plugins.java.maingenerator.XmlSchemaUtils;
import com.db4o.odbgen.plugins.java.maingenerator.typedxmlschema.TSClass;
import com.db4o.odbgen.plugins.java.maingenerator.typedxmlschema.TSField;
import com.db4o.odbgen.plugins.java.maingenerator.typedxmlschema.TSMethod;


/**
 * Implements the notnull constraint.
 * @author liviu
 *
 */
public class Notnull implements Plugin{
	//TODO: Warning: Notnull constraint does not work at this time, unless 
	//transparent activation is enabled.
	//In order to solve this, the object received by the callback must be activated.
	//But in the callback we don't have access to an ObjectContainer
	//Currently it is possible to do this if a class implementing EventListener4 is created.

	private MainGenerator _mainGenerator;

	/**
	 * Contains the list of standard templates used by {@link Notnull}.
	 * @author liviug
	 */
	private enum Templates implements TemplateList{
		NOTNULL(Templates.path, "notnull.vm");
		private static final String path = "standardPlugins/java/database";

		
		private String _path;
		private String _name;

		Templates(String path, String name){
			this._path = path;
			this._name = name;
		}
		
		/**
		 * Returns the path and the name of the template.
		 */
		public String getQualifiedName() {
			return this._path+"/"+this._name;
		}
	}
	
	public static class NotnullFields{
		TSClass _parentClass;
		List<TSField> _notnullFields;
		public NotnullFields(TSClass parentClass, List<TSField> notnullFields){
			this._parentClass= parentClass;
			this._notnullFields = notnullFields;
		}
		
		public TSClass getParentClass(){
			return this._parentClass;
		}
		public List<TSField> getNotnullFields(){
			return this._notnullFields;
		}
	}
	
	/**
	 * See {@link Plugin} for details.
	 */
	@Override
	public List<Class<? extends Plugin>> getPluginDependencies() {
		ArrayList<Class<? extends Plugin>> res = new ArrayList<Class<? extends Plugin>>();
		res.add(MainGenerator.class);
		return res;
	}

	/**
	 * See {@link Plugin} for details.
	 */
	@Override
	public void initialize(MainGeneratorPlugin mainGenerator) {
		this._mainGenerator = (MainGenerator)mainGenerator;
		
	}

	/**
	 * See {@link Plugin} for details.
	 */
	@Override
	public void start() throws OdbgenException {

		
		
		//Build a list of fields that have to be constrained.
		List<NotnullFields> classes = new ArrayList<NotnullFields>();
		for(TSClass tsClass : this._mainGenerator.getTypedXmlSchema().getClasses()){
			List<TSField> fields = new ArrayList<TSField>();
			for(TSField field : tsClass.getFields()){
				//The field must belong to XmlSchema.
				if(field.getElement()==null) continue;
				if(field.getElement().getBooleanChildValue(NotnullXmlSchemaElement.NOTNULL.getName())){
					fields.add(field);
				}
			}
			if(fields.size()>0){
				classes.add(new NotnullFields(tsClass, fields));
			}
		}
		
		//Build the content
		Template template = TemplatesUtils.getTemplateJar(Notnull.Templates.NOTNULL.getQualifiedName(), this._mainGenerator.getStandardTemplatesJarName());
		VelocityContext context = new VelocityContext();
		context.put("notnullClassses", classes);
		context.put("typedXmlSchema", this._mainGenerator.getTypedXmlSchema());
		String content = TemplatesUtils.merge(template, context);

		//Add the constraint code to the propper handlers.
		//We are not adding the constraints to the server callbacks
		//because 'creating' and 'updating' callbacks do not work server side.
		TSMethod callbackMethod;
		
		callbackMethod = this._mainGenerator.getSchemaUtils().getStandardCallback(
				XmlSchemaUtils.ExternalCallbackType.UPDATING, 
				XmlSchemaUtils.Scenario.CLIENT
				);
		callbackMethod.getContent().append(content);
		
		callbackMethod = this._mainGenerator.getSchemaUtils().getStandardCallback(
				XmlSchemaUtils.ExternalCallbackType.UPDATING, 
				XmlSchemaUtils.Scenario.EMBEDDED
				);
		callbackMethod.getContent().append(content);
		
		callbackMethod = this._mainGenerator.getSchemaUtils().getStandardCallback(
				XmlSchemaUtils.ExternalCallbackType.CREATING, 
				XmlSchemaUtils.Scenario.CLIENT
				);
		callbackMethod.getContent().append(content);
		
		callbackMethod = this._mainGenerator.getSchemaUtils().getStandardCallback(
				XmlSchemaUtils.ExternalCallbackType.CREATING, 
				XmlSchemaUtils.Scenario.EMBEDDED
				);
		callbackMethod.getContent().append(content);
	}


	/**
	 * See {@link Plugin} for details.
	 */
	@Override
	public boolean supportsTargetLanguage(TargetLanguage targetLanguage) {
		if(targetLanguage == TargetLanguage.JAVA){
			return true;
		}
		return false;
	}

}
